import { CCCAgentsError } from '../CCCAgentsError';
import { CCInteger, _decorator } from 'cc';

const { ccclass, property } = _decorator;
/**
 * 行为规范
 * 定义执行器系统(Actuator system)要使用的行为结构
 */
@ccclass('ActionSpec')
export class ActionSpec {
	/**
	 * agent可连执行的连续行为（continuous actions）数
	 */
	@property({ type: CCInteger, serializable: true, visible: true, displayName: 'Continuous Actions' })
	private _numContinuousActions: number;
	public get numContinuousActions(): number {
		return this._numContinuousActions;
	}
	public set numContinuousActions(value: number) {
		this._numContinuousActions = value;
	}

	/**
	 * 离散行为的分支大小
	 */
	@property({ type: [CCInteger], serializable: true, visible: true, displayName: 'Discrete Branches' })
	public branchSizes: Array<number>;

	/**
	 * 代理(Agent)可执行的离散行为的分支数
	 */
	public get numDiscreteActions(): number {
		return this.branchSizes?.length;
	}

	private _sumOfDiscreteBranchSize: number = 0;
	public get sumOfDiscreteBranchSize(): number {
		return this._sumOfDiscreteBranchSize;
	}
	public set sumOfDiscreteBranchSize(value: number) {
		this._sumOfDiscreteBranchSize = value;
	}

	/**
	 * 创建一个包含可用行为数量的行为规范
	 * @param numActions 可用的连线行为数量
	 * @returns 一个连线行为规范
	 */
	public static makeContinuous(numActions: number): ActionSpec {
		const actuatorSpace = new ActionSpec(numActions);
		return actuatorSpace;
	}

	/**
	 * 使用标识操作空间的分支大小数组创建离散行为规范
	 * @param branchSizes 离散行为的数量
	 * @returns 一个用分支数组大小初始化的离散行为规范
	 */
	public static makeDiscrete(branchSizes: Array<number>) {
		const actuatorSpace = new ActionSpec(0, branchSizes);
		return actuatorSpace;
	}

	constructor(numContinuousActions: number = 0, discreteBranchSizes?: Array<number>) {
		this._numContinuousActions = numContinuousActions;
		this.branchSizes = discreteBranchSizes ?? [];
	}

	/**
	 * 检查行为规范是否使用所有连续或所有离散行为。
	 *
	 */
	checkAllContinuousOrDiscrete() {
		if (this.numContinuousActions > 0 && this.numDiscreteActions > 0) {
			throw new CCCAgentsError(
				`Action spaces with both continuous and discrete actions are not supported by the trainer.
        ActionSpecs must be all continuous or all discrete.`
			);
		}
	}

	/**
	 * 组合行为规范列表，并根据需要分配分支大小的数组
	 * @param specs 需要组合的行为规范列表
	 * @returns
	 */
	public static combine(specs: Array<ActionSpec>): ActionSpec {
		let numContinuous = 0;
		let numDiscrete = 0;
		specs.forEach((spec) => {
			numContinuous += spec.numContinuousActions;
			numDiscrete += spec.numDiscreteActions;
		});

		if (numDiscrete <= 0) {
			return ActionSpec.makeContinuous(numContinuous);
		}

		let branchSizes = new Array<number>(numDiscrete);
		for (let i = 0; i < specs.length; i++) {
			const spec = specs[i];
			if (spec.branchSizes.length == 0) {
				continue;
			}

			branchSizes = branchSizes.concat(spec.branchSizes);
		}
		return new ActionSpec(numContinuous, branchSizes);
	}
}
